請搭配 D13 - 樹狀搜尋問題 非同步版 實作篇 閱讀
結構化程式設計中,我認為有兩點值得改進
async function getNameById(target:string, rootId: string){
let queue = [rootId]
while(queue.length > 0){
try {
let node = await getNode(queue[0]) // (1)
if(node.id === target){
return node.text
}
for(let id of node.childrenIds){
queue.push(id)
}
} catch (e) { // (2)
console.error(`get id ${queue[0]} failed, skip this node`)
}
queue.shift()
}
return undefined
}
效能問題,用一般直覺的寫法,很容易忘記優化其實可以平行處理的流程。
錯誤處理,在 Java 等強型別語言中,我們可以明確知道函式可能拋出哪些錯誤、自然也就可以晚點再處理它,然而在 JS 中錯誤處理相對其他語言很不成熟
函數式程式設計同樣也存在問題
const getNameById = (target:string) => async(id:string):Promise<string|undefined> =>
getNode(id).then((result)=> // (1)
result._tag==="Node" && result.id===target ? result.text : // (2)
result._tag==="NodeNotFoundError" ? undefined : // (3)
Promise.all(result.childrenIds.map(getNameById(target)))
.then(result=>result.find(text=>text!==undefined) )
)
.then
,更容易理解 async
await
if else
,禁止 if else
導致程式更難讀side effect
(例如 console error) 導致失去在流程中進行錯誤處理的彈性const getNameById = (target:string) => async(id:string):Promise<string|undefined> => {
const result = await getNode(id)
if(result._tag==="NodeNotFoundError") {
console.error(`get id ${id} failed, skip this node`)
return undefined
}
if(result._tag==="Node" && result.text===target) return result.text
const children = await Promise.all(result.childrenIds.map(getNameById(target)))
return children.find(text=>text!==undefined)
}
有時候函數式設計本身沒有錯,錯的是「企圖用函數式語法改變大家的寫作習慣」,如果能用結構化的方式達成函數式程式想要達成的目標(不可變更性、副作用的管理、非同步函式的管理),其實也沒有不這麼做的理由 ?